package org.dg.iag.util.scheduler;

import static org.quartz.JobBuilder.*;
import static org.quartz.SimpleScheduleBuilder.*;
import static org.quartz.CronScheduleBuilder.*;
import static org.quartz.CalendarIntervalScheduleBuilder.*;
import static org.quartz.TriggerBuilder.*;
import static org.quartz.DateBuilder.*;
import static org.quartz.DateBuilder.IntervalUnit.*;
import static org.quartz.TriggerKey.*;
import static org.quartz.JobKey.*;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Hashtable;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.log4j.Logger;
import org.dg.iag.entity.Settings;
import org.dg.iag.entity.SettingsInstance;
import org.dg.iag.server.SettingsServerResource;
import org.dg.iag.util.ApplicationConstants;
import org.quartz.JobDetail;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.Trigger;
import org.quartz.impl.StdSchedulerFactory;

public class SchedulerManager {

	public static Logger logger	= Logger.getLogger(SchedulerManager.class);
	
	private static SchedulerManager singleton	= null;
	
	private Map<JobDetail,Trigger> jobs ;
	private Scheduler scheduler ;

	
	private SchedulerManager() {
		try {
			jobs			= new Hashtable<JobDetail, Trigger>();
			this.scheduler	= StdSchedulerFactory.getDefaultScheduler();
		} catch (SchedulerException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	public static SchedulerManager getInstance () {
		if ( singleton == null ) {
			singleton = new SchedulerManager();
		}
		return singleton;
	}
	
	public synchronized void createJobs () {
		Settings settings		= new SettingsServerResource().getElement();
		if ( settings != null && settings.getSettingsInstances() != null && settings.getSettingsInstances().size() > 0 ) {
			jobs.clear();
			for ( SettingsInstance si: settings.getSettingsInstances() ) {
				if ( si.getEnabled() ) {
					Trigger trigger	= null;
					JobDetail job	= null;
					
					int minute		= 0, hour = 0;
					
					try {
						SimpleDateFormat sdf	= new SimpleDateFormat("h:mm a");
						Date d					= sdf.parse(si.getTime() );
						Calendar cal			= Calendar.getInstance();
						cal.setTime(d);
						minute					= cal.get(Calendar.MINUTE);
						hour					= cal.get(Calendar.HOUR_OF_DAY);
					}
					catch (Exception e) {
						e.printStackTrace();
						continue;
					}
					
					if ( ApplicationConstants.SETTINGS_PERIOD_DAILY.equals(si.getHowOften()) ) {
						trigger = newTrigger() 
				             .withIdentity(triggerKey("settings_" + si.getId(), "Settings Triggers"))
				             .withSchedule(dailyAtHourAndMinute( hour, minute )
				                 )
				             .startAt(futureDate(10, SECOND))
				             .build();
					}
					if ( ApplicationConstants.SETTINGS_PERIOD_WEEKLY.equals(si.getHowOften()) ) {
						trigger = newTrigger() 
				             .withIdentity(triggerKey("settings_" + si.getId(), "Settings Triggers"))
				             .withSchedule( weeklyOnDayAndHourAndMinute(si.getDayOfWeek(), hour, minute )
				                 )
				             .startAt(futureDate(10, SECOND))
				             .build();
					}
					if ( ApplicationConstants.SETTINGS_PERIOD_WEEKLY.equals(si.getHowOften()) ) {
						trigger = newTrigger() 
				             .withIdentity(triggerKey("settings_trigger_" + si.getId(), "Settings Triggers"))
				             .withSchedule( monthlyOnDayAndHourAndMinute(si.getDayOfMonth(), hour, minute )
				                 )
				             .startAt(futureDate(10, SECOND))
				             .build();
					}
					if ( ApplicationConstants.SETTINGS_REFRESH_ALL.equals( si.getRefreshType() )  ) {
						job		= newJob(ImportAllJob.class)
					            .withIdentity("import_all_job_"+si.getId(), "Settings Jobs")
					            .build();
					}
					if ( ApplicationConstants.SETTINGS_REFRESH_FAILED.equals( si.getRefreshType() )  ) {
						job		= newJob(ImportFailedJob.class)
					            .withIdentity("import_failed_job_"+si.getId(), "Settings Jobs")
					            .build();
					}
					if ( job != null && trigger != null) {
						logger.info("Adding job for SettingsInstance " + si.getId() + " period: " + si.getHowOften() + " time: " + hour + ":" + minute );
						jobs.put(job, trigger);
					}
				}
			}
		}
	}
	
	public synchronized void startManager () {
		  try {
//			  logger.info("Starting schedule manager");
//	            // Grab the Scheduler instance from the Factory 
//			  Trigger trigger = newTrigger() 
//			             .withIdentity(triggerKey("myTrigger", "myTriggerGroup"))
//			             .withSchedule(simpleSchedule()
//			                 .withIntervalInHours(1)
//			                 .repeatForever())
//			             .startAt(futureDate(10, SECOND))
//			             .build();
//			  JobDetail job = newJob(ImportAllJob.class)
//			            .withIdentity("job1", "group1")
//			            .build();
			  
	            
//	            scheduler.scheduleJob(job, trigger);

	            // and start it off
			  	for ( Entry<JobDetail, Trigger> entry: jobs.entrySet() ) {
			  		this.scheduler.scheduleJob(entry.getKey(), entry.getValue() );
			  	}
			  	if ( !this.scheduler.isStarted() )
			  		this.scheduler.start();

	        } catch (SchedulerException se) {
	            se.printStackTrace();
	        }
	}
	
	public void shutdownManager() {
		try {
			scheduler.shutdown();
			singleton	= new SchedulerManager();
		} catch (SchedulerException e) {
			e.printStackTrace();
		}
	}
	
	
	public synchronized void clearManager() {
		try {
			for (Entry<JobDetail, Trigger> entry: jobs.entrySet() ) {
				this.scheduler.unscheduleJob(entry.getValue().getKey() );
				this.scheduler.deleteJob(entry.getKey().getKey());
			}
			scheduler.clear();
		} catch (SchedulerException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	public synchronized void reboot () {
			clearManager(); 
			createJobs();
			startManager();
	}
}
